<?php
// +----------------------------------------------------------------------
// | SwiftPHP [ JUST DO ONE THING WELL ]
// +----------------------------------------------------------------------
// | Copyright (c) 2015-2017  http://swiftphp.zhongwenyu.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhongwenyu <zhongwenyu1987@163.com> <http://www.zhongwenyu.com>
// +----------------------------------------------------------------------
// | Times: 2017/2/16 16:12
// +----------------------------------------------------------------------
namespace swift;

class App{
    protected static $init = false;    //是否初始化过
    protected static $suffix = false;    //应用类库后缀
    protected static $debug = false;    //应用调试模式
    public static $namespace = 'app';    //应用命名空间
    protected static $dispatch;    //应用调度信息，即访问哪个模块、哪个方法等
    public static $modulePath;   //当前模块路径
    protected static $file = [];   // 加载额外文件

    public static function run(Request $request = null){
        is_null($request) && $request = Request::instance();    //返回Request类

        $config = self::initCommon();
        if($config['html_cache']['on'] && strtolower($config['html_cache']['type']) == 'file' && Htmlcache::has()){
            // 页面静态缓存检测
            $data = Htmlcache::read();
        }else {
            $dispatch = self::$dispatch;    //获取应用调度信息
            if (empty($dispatch)) {
                $dispatch = self::routeCheck($request, $config);
            }
            // 记录当前调度信息
            $request->dispatch($dispatch);
            switch ($dispatch['type']) {
                case 'module':
                    // 模块/控制器/操作
                    $data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null);
                    break;
                default:
                    throw new \Exception('dispatch type not support');
            }
        }

        // 清空类的实例化
        //Loader::clearInstance();

        // 输出数据到客户端
        if($data instanceof Response){
            $response = $data;
        }elseif(!is_null($data)){
            $isAjax = $request->isAjax();
            $type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type');
            $response = Response::create($data , $type);
        }else{
            $response = Response::create();
        }

        return $response;
    }

    /**
     * 执行模块
     * @access public
     * @param array $result 模块/控制器/操作
     * @param array $config 配置参数
     * @param bool  $convert 是否自动转换控制器和操作名
     * @return mixed
     */
    public static function module($result, $config, $convert = null){
        if(is_string($result)){
            $result = explode('/',$result);
        }
        $request = Request::instance();
        $module = strip_tags($result[0] ? strtolower($result[0]) : $config['default_module']);   // 剥去html标签
        $available = false;   // 是否执行
        if(!in_array($module , $config['deny_module_list']) && is_dir(APP_PATH . $module)){
            // 是否拒绝访问
            $available = true;
        }
        if($module && $available){
            $request->module($module);  // 设置当前运行的模块名
            self::initModule($module);  // 初始化模块
        }else{
            throw new \Exception("模块 $module 拒绝访问！");
        }
        // 当前模块路径
        self::$modulePath = APP_PATH . ($module ? $module . DS : '');
        // 获取控制器名
        $controller = strip_tags($result[1] ? strtolower($result[1]) : $config['default_controller']);
        // 获取操作名
        $actionName = strip_tags($result[2] ? strtolower($result[2]) : $config['default_action']);
        // 设置当前请求的控制器、操作
        $request->controller($controller)->action($actionName);
        $instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
        if(is_null($instance)){
            throw new \Exception($controller." 控制器不存在！");
        }
        $vars = [];

        // 初始化运行
        if(is_callable([$instance,'__initialize'])){
            $res = Loader::invokeMethod([$instance,'__initialize']);
            if(isset($res)){
                return $res;
            }
        }

        if(is_callable([$instance,$actionName])){
            // 执行操作方法
            $call = [$instance, $actionName];
        }elseif(is_callable([$instance,'_empty'])){
            // 执行空操作
            $call = [$instance, $actionName];
            $vars[] = $actionName;
        }else{
            throw new \Exception($actionName." 操作不存在！");
        }
        return Loader::invokeMethod($call, $vars);
    }

    /**
     * URL路由检测（根据PATH_INFO)
     * @access public
     * @param  \swift\Request $request
     * @param  array          $config
     * @return array
     * @throws \swift\Exception
     */
    public static function routeCheck($request, array $config){
        $path = $request->path();
        $depr   = $config['pathinfo_depr'];
        // 路由检测
        $check = true;
        if ($check) {
            // 开启路由
            if (is_file(RUNTIME_PATH . 'route.php') && !Config::get('app_debug')) {
                // 非调试模式下读取路由缓存
                $rules = include RUNTIME_PATH . 'route.php';
                if (is_array($rules)) {
                    Route::rules($rules);
                }
            } else {
                $files = $config['route_config_file'];
                foreach ($files as $file) {
                    if (is_file(CONF_PATH . $file . CONF_EXT)) {
                        // 导入路由配置
                        $rules = include CONF_PATH . $file . CONF_EXT;
                        if (is_array($rules)) {
                            Route::import($rules);
                        }
                    }
                }
            }
            // 路由检测（根据路由定义返回不同的URL调度）
            $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
            return $result;
        }
    }

    /**
     * 初始化应用
     */
    public static function initCommon(){
        if (empty(self::$init)){
            $config = self::initModule();
            self::$suffix = $config['class_suffix'];    //设置应用类库后缀
            self::$debug = Config::get('app_debug');    //是否开启调试模式
            if (!self::$debug) {
                ini_set('display_errors', 'Off');    //ini_set()设定php.ini指定参数的值
            }
            self::$namespace = $config['app_namespace'];

            Loader::addNamespace($config['app_namespace'], APP_PATH);    //注册应用命名空间

            // 加载助手函数文件
            if(is_file($helper = SWIFT_PATH . 'helper' . EXT)){
                include $helper;
            }

            // 加载额外文件
            if (!empty($config['extra_file_list'])) {
                foreach ($config['extra_file_list'] as $file) {
                    $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
                    if (is_file($file) && !isset(self::$file[$file])) {
                        include $file;
                        self::$file[$file] = true;
                    }
                }
            }

            date_default_timezone_set($config['default_timezone']);    // 设置系统时区

            self::$init = true;
        }
        return Config::get();
    }

    /**
     * 初始化应用或模块
     * @access public
     * @param string $module 模块名
     * @return array
     */
    public static function initModule($module = ''){
        $module = $module ? $module . DS : '';    //定位模块目录

        //加载初始化文件
        $path = APP_PATH . $module;
        Config::load(CONF_PATH . $module . 'config' . CONF_EXT);  //加载模块配置文件
        Config::load(CONF_PATH . $module . 'database' . CONF_EXT, 'database');    //读取数据库配置文件
        if (is_file($path . 'common' . EXT)) {    // 加载公共文件
            include $path . 'common' . EXT;
        }

        // 加载额外文件
        if (!empty(Config::get('extra_file_list'))) {
            foreach (Config::get('extra_file_list') as $file) {
                $file = strpos($file, '.') ? $file : APP_PATH  . $file . EXT;
                if (is_file($file) && !isset(self::$file[$file])) {
                    include $file;
                    self::$file[$file] = true;
                }
            }
        }

        return Config::get();
    }

    /**
     * 调试模式下，显示响应速度
     */
    public static function runtime(){
        if(Config::get('app_debug') && !Request::instance()->isAjax()){
            $time = microtime(true) - START_TIME;
            echo "<div style='position:fixed;bottom:0;right:0;background:#000;color:#fff;font-size:14px;text-align:center;padding:5px 15px'>". $time ."</div>";
        }
    }
}